-
Notifications
You must be signed in to change notification settings - Fork 314
Mute muted users (Chris's revision, 2 of 2) #1561
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
9c82301
to
0acc63b
Compare
Revision pushed; this one also excludes muted users from the new new-DM UI, which landed after my last revision. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I've read so far the first three commits:
8af150c recent-dms: Exclude DM conversations where all other recipients are muted
06937e2 msglist [nfc]: Place _allMessagesVisible
right after _messageVisible
8339c64 msglist [nfc]: s/VisibilityEffect/UserTopicVisibilityEffect/
and the next one except for its tests:
898a2ad msglist: In combined/mentions/starred, exclude DMs if all recipients muted
Generally all looks good; a few small comments below.
That leaves four more commits that I haven't read (though I've skimmed them):
619eb24 autocomplete: Exclude muted users from user-mention autocomplete
87df780 msglist [nfc]: Represent nobody-is-typing as null, not empty list
c40cb89 msglist: Exclude muted users from typing-status text
0acc63b new-dm: Exclude muted users
(false, true) => VisibilityEffect.unmuted, | ||
(true, false) => VisibilityEffect.muted, | ||
_ => VisibilityEffect.none, | ||
(false, true) => UserTopicVisibilityEffect.unmuted, | ||
(true, false) => UserTopicVisibilityEffect.muted, | ||
_ => UserTopicVisibilityEffect.none, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is awkwardly verbose, but we're about to add another kind of
visibility effect, and I think the code will end up clearer if we
make a separate enum for it.
Yeah, agreed.
Some of the verbosity (like in these lines) will at least get resolved by the upcoming Dart feature of "dot shorthands", expected later this year:
https://github.com/dart-lang/language/blob/main/working/3616%20-%20enum%20value%20shorthand/proposal-simple-lrhn.md
I believe with that feature we'll be able to say just .unmuted
, .muted
, .none
here, without the name of the enum type.
lib/model/message_list.dart
Outdated
!store.shouldMuteDmConversation(DmNarrow( | ||
allRecipientIds: allRecipientIds, selfUserId: store.selfUserId)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make another DmNarrow constructor for this:
!store.shouldMuteDmConversation(DmNarrow( | |
allRecipientIds: allRecipientIds, selfUserId: store.selfUserId)), | |
!store.shouldMuteDmConversation(DmNarrow.ofConversation( | |
message.conversation, selfUserId: store.selfUserId)), |
Compare the comment at the top of that class:
// Zulip has many ways of representing a DM conversation; for example code
// handling many of them, see zulip-mobile:src/utils/recipient.js .
// Please add more constructors and getters here to handle any of those
// as we turn out to need them.
class DmNarrow extends Narrow implements SendableNarrow {
It'd be bad if this logic did something inefficient like copy the list of recipient IDs, because we're doing this for potentially a large fraction of the messages in this feed (if a user uses lots of DMs). I believe this in fact doesn't make such a copy — and a constructor on DmNarrow is a good place to centralize coming to that conclusion.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Huh…it looks like DmNarrow.ofMessage
, though, does copy the list of recipient IDs:
factory DmNarrow.ofMessage(MessageBase<DmConversation> message, {
required int selfUserId,
}) {
return DmNarrow(
allRecipientIds: List.unmodifiable(message.conversation.allRecipientIds),
selfUserId: selfUserId,
);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm indeed.
It looks like we don't currently call that one in any place that's as performance-sensitive as this. But it's a bit of a pitfall lying around.
lib/model/message_list.dart
Outdated
switch (narrow) { | ||
case CombinedFeedNarrow(): | ||
case ChannelNarrow(): | ||
case MentionsNarrow(): | ||
case StarredMessagesNarrow(): | ||
return false; | ||
|
||
case TopicNarrow(): | ||
case DmNarrow(): | ||
return true; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's keep these cases in the same order as in _messageVisible, for ease of comparing the logic. It's important that the two align (that's this method's job); and it's potentially a bit subtle to confirm that, so it's good to make it as easy as possible.
It's fine for this to have two separate groups of cases that return the same thing.
MutedUsersVisibilityEffect _mutedUsersEventCanAffectVisibility(MutedUsersEvent event) { | ||
switch(narrow) { | ||
case CombinedFeedNarrow(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similarly, put the cases in the same order as in _messageVisible
final union = setUnion(sortedOld, sortedNew); | ||
|
||
final willMuteSome = sortedOld.length < union.length; | ||
final willUnmuteSome = sortedNew.length < union.length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Neat, yeah, this logic works.
This is awkwardly verbose, but we're about to add another kind of visibility effect, and I think the code will end up clearer if we make a separate enum for it. Greg points out that an upcoming Dart feature "dot shorthands" should help with this verbosity: zulip#1561 (comment)
0acc63b
to
ceb4e0f
Compare
Thanks! Revision pushed. |
Thanks! Those changes look good. There's a CI failure in build_runner. (And there are parts of the branch I still need to read, per #1561 (review) .) |
ceb4e0f
to
f3f4df2
Compare
This is awkwardly verbose, but we're about to add another kind of visibility effect, and I think the code will end up clearer if we make a separate enum for it. Greg points out that an upcoming Dart feature "dot shorthands" should help with this verbosity: zulip#1561 (comment)
I just rebased atop current |
Like userDisplayName does. And remove a null-check `store.getUser(userId)!` at one of the callers... I think that's *probably* NFC, for the reason given in a comment ("must exist because UserMentionAutocompleteResult"). But it's possible this is actually a small bugfix involving a rare race involving our batch-processing of autocomplete results. Related: zulip#716
We've now centralized on store.userDisplayName and store.senderDisplayName for all the code that's responsible for showing a user's name on the screen, except for a few places we use `User.fullName` for (a) the self-user and (b) to create an @-mention for the compose box. The "(unknown user)" and upcoming "Muted user" placeholders aren't needed for (a) or (b).
(Done by adding an is-muted condition in store.userDisplayName and store.senderDisplayName, with an opt-out param.) If Chris is muted, we'll now show "Muted user" where before we would show "Chris Bobbe", in the following places: - Message-list page: - DM-narrow app bar. - DM recipient headers. - The sender row on messages. This and message content will get more treatment in a separate commit. - Emoji-reaction chips on messages. - Typing indicators ("Muted user is typing…"), but we'll be excluding muted users, coming up in a separate commit. - Voter names in poll messages. - DM items in the Inbox page. (Messages from muted users are automatically marked as read, but they can end up in the inbox if you un-mark them as read.) - The new-DM sheet, but we'll be excluding muted users, coming up in a separate commit. - @-mention autocomplete, but we'll be excluding muted users, coming up in a separate commit. - Items in the Direct messages ("recent DMs") page. We'll be excluding DMs where everyone is muted, coming up in a separate commit. - User items in custom profile fields. We *don't* do this replacement in the following places, i.e., we'll still show "Chris Bobbe" if Chris is muted: - Sender name in the header of the lightbox page. (This follows web.) - The "hint text" for the compose box in a DM narrow: it will still say "Message @chris Bobbe", not "Message @muted user". (This follows web.) - The user's name at the top of the Profile page. - We won't generate malformed @-mention syntax like @_**Muted user|13313**. Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
We're free to rename these because they don't correspond to named variables in the Figma. These more general names will be used for an avatar placeholder for muted users, coming up. Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
…able (Done by adding an is-muted condition in Avatar and AvatarImage, with an opt-out param. Similar to how we handled users' names in a recent commit.) If a user is muted, we'll now show a placeholder where before we would have shown their real avatar, in the following places: - The sender row on messages in the message list. This and message content will get more treatment in a separate commit. - @-mention autocomplete, but we'll be excluding muted users, coming up in a separate commit. - User items in custom profile fields. - 1:1 DM items in the Direct messages ("recent DMs") page. But we'll be excluding those items there, coming up in a separate commit. We *don't* do this replacement in the following places, i.e., we'll still show the real avatar: - The header of the lightbox page. (This follows web.) - The big avatar at the top of the profile page. Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
…erRow No-op because the child Flexible -> GestureDetector -> Row has the default MainAxisSize.max, filling the available space, leaving none that would be controlled by spaceBetween.
For muted-users, coming up. This was ad hoc for mobile, for the "Reveal message" button on a message from a muted sender: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=6092-50786&m=dev
For muted-users, coming up. This is consistent with the ad hoc design for muted-users, but also consistent with the component in "Zulip Web UI Kit". (Modulo the TODO for changing icon-to-label gap from 8px to 6px; that's tricky with the Material widget we're working with.)
… type For muted-users, coming up. Figma: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=6092-50795&m=dev
Figma design: https://www.figma.com/design/1JTNtYo9memgW7vV6d0ygq/Zulip-Mobile?node-id=6089-28385&t=28DdYiTs6fXWR9ua-0 Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
…uted Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
This is solely for a better order.
This is awkwardly verbose, but we're about to add another kind of visibility effect, and I think the code will end up clearer if we make a separate enum for it. Greg points out that an upcoming Dart feature "dot shorthands" should help with this verbosity: zulip#1561 (comment)
…muted In the future, this should apply to the ReactionsNarrow from the Web app too, once we have it. Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
Co-authored-by: Sayed Mahmood Sayedi <[email protected]>
Just to skip the bit of extra work of creating new lists.
This completes the planned work for muting muted users (hooray!). Fixes: zulip#296
f3f4df2
to
5438a65
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, and read those remaining 4 commits. Just small comments below.
// TODO(#236) test email too, not just name | ||
if (!user.isActive) return false; | ||
if (store.isUserMuted(user.userId)) return false; | ||
|
||
return _testName(user, cache); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit:
// TODO(#236) test email too, not just name | |
if (!user.isActive) return false; | |
if (store.isUserMuted(user.userId)) return false; | |
return _testName(user, cache); | |
if (!user.isActive) return false; | |
if (store.isUserMuted(user.userId)) return false; | |
// TODO(#236) test email too, not just name | |
return _testName(user, cache); |
The comment was already a bit separated from what it applied to, so perhaps should have been moved down there sooner; but this would widen the separation, so let's fix it.
(Can stay in the same commit, no need to make a separate prep commit.)
final typistIds = model!.typistIdsInNarrow(narrow); | ||
if (typistIds.isEmpty) return const SizedBox(); | ||
final text = switch (typistIds.length) { | ||
if (typistIds == null) return const SizedBox(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
msglist [nfc]: Represent nobody-is-typing as null, not empty list
Just to skip the bit of extra work of creating new lists.
Hmm, this feels like the API gets a bit more complicated from the caller's perspective. What if typistIds
is empty, and why is there a different possibility which is that it's null?
(In reality I think it's never empty after this change — _removeTypist removes the sub-maps when empty. But that's not obvious from the outside.)
Iterable<int> typistIdsInNarrow(SendableNarrow narrow) => | ||
_timerMapsByNarrow[narrow]?.keys ?? []; | ||
Iterable<int>? typistIdsInNarrow(SendableNarrow narrow) => | ||
_timerMapsByNarrow[narrow]?.keys; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An alternate way of not creating new lists each time:
Iterable<int> typistIdsInNarrow(SendableNarrow narrow) =>
_timerMapsByNarrow[narrow]?.keys ?? const [];
final filteredTypistIds = typistIds | ||
.whereNot((userId) => store.isUserMuted(userId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: can use a tear-off, which is shorter and I think here a bit easier to read:
final filteredTypistIds = typistIds | |
.whereNot((userId) => store.isUserMuted(userId)); | |
final filteredTypistIds = typistIds.whereNot(store.isUserMuted); |
expected: 'Third User and Fourth User are typing…'); | ||
await store.setMutedUsers([eg.thirdUser.userId]); | ||
await tester.pump(); | ||
check(tester.widget<Text>(finder)).data.equals('Fourth User is typing…'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's put this in a separate test case. This one (or this pair of them) is pretty long and a bit unfocused already.
The new test case can be for just a topic narrow; I don't think it needs to exercise both kinds of conversations.
final sansMuted = store.allUsers | ||
.whereNot((User user) => store.isUserMuted(user.userId)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: omit boring types
final sansMuted = store.allUsers | |
.whereNot((User user) => store.isUserMuted(user.userId)); | |
final sansMuted = store.allUsers | |
.whereNot((user) => store.isUserMuted(user.userId)); |
Stacked on #1560.
Many thanks again to @sm-sayedi for your work on this! As with #1560, this is meant to take #1429 across the finish line in time for the upcoming app launch. Thanks again for making sure I had your latest revision of #1429 before you left for vacation. 🙂
In these new commits:
Fixes: #296